{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用OpenAI API"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "GPT有封装好的接口，我们简单封装即可。目前GPT embedding mode有三种，性能如下所示：\n",
    "|模型 | 每美元页数 | [MTEB](https://github.com/embeddings-benchmark/mteb)得分 | [MIRACL](https://github.com/project-miracl/miracl)得分|\n",
    "| --- | --- | --- | --- |\n",
    "|text-embedding-3-large|9,615|54.9|64.6|\n",
    "|text-embedding-3-small|62,500|62.3|44.0|\n",
    "|text-embedding-ada-002|12,500|61.0|31.4|\n",
    "* MTEB得分为embedding model分类、聚类、配对等八个任务的平均得分。\n",
    "* MIRACL得分为embedding model在检索任务上的平均得分。  \n",
    "\n",
    "从以上三个embedding model我们可以看出`text-embedding-3-large`有最好的性能和最贵的价格，当我们搭建的应用需要更好的表现且成本充足的情况下可以使用；`text-embedding-3-small`有着较好的性能跟价格，当我们预算有限时可以选择该模型；而`text-embedding-ada-002`是OpenAI上一代的模型，无论在性能还是价格都不如及前两者，因此不推荐使用。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "ename": "RateLimitError",
     "evalue": "Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mRateLimitError\u001b[0m                            Traceback (most recent call last)",
      "Cell \u001b[0;32mIn[1], line 31\u001b[0m\n\u001b[1;32m     25\u001b[0m     response \u001b[38;5;241m=\u001b[39m client\u001b[38;5;241m.\u001b[39membeddings\u001b[38;5;241m.\u001b[39mcreate(\n\u001b[1;32m     26\u001b[0m         \u001b[38;5;28minput\u001b[39m\u001b[38;5;241m=\u001b[39mtext,\n\u001b[1;32m     27\u001b[0m         model\u001b[38;5;241m=\u001b[39mmodel\n\u001b[1;32m     28\u001b[0m     )\n\u001b[1;32m     29\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m response\n\u001b[0;32m---> 31\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mopenai_embedding\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtext\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m要生成 embedding 的输入文本，字符串形式。\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n",
      "Cell \u001b[0;32mIn[1], line 25\u001b[0m, in \u001b[0;36mopenai_embedding\u001b[0;34m(text, model)\u001b[0m\n\u001b[1;32m     22\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m model \u001b[38;5;241m==\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m     23\u001b[0m     model\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mtext-embedding-3-small\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m---> 25\u001b[0m response \u001b[38;5;241m=\u001b[39m \u001b[43mclient\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43membeddings\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcreate\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m     26\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtext\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m     27\u001b[0m \u001b[43m    \u001b[49m\u001b[43mmodel\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmodel\u001b[49m\n\u001b[1;32m     28\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m     29\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m response\n",
      "File \u001b[0;32m/opt/anaconda3/envs/llm-rag/lib/python3.12/site-packages/openai/resources/embeddings.py:124\u001b[0m, in \u001b[0;36mEmbeddings.create\u001b[0;34m(self, input, model, dimensions, encoding_format, user, extra_headers, extra_query, extra_body, timeout)\u001b[0m\n\u001b[1;32m    118\u001b[0m         embedding\u001b[38;5;241m.\u001b[39membedding \u001b[38;5;241m=\u001b[39m np\u001b[38;5;241m.\u001b[39mfrombuffer(  \u001b[38;5;66;03m# type: ignore[no-untyped-call]\u001b[39;00m\n\u001b[1;32m    119\u001b[0m             base64\u001b[38;5;241m.\u001b[39mb64decode(data), dtype\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mfloat32\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[1;32m    120\u001b[0m         )\u001b[38;5;241m.\u001b[39mtolist()\n\u001b[1;32m    122\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m obj\n\u001b[0;32m--> 124\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_post\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    125\u001b[0m \u001b[43m    \u001b[49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[38;5;124;43m/embeddings\u001b[39;49m\u001b[38;5;124;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m    126\u001b[0m \u001b[43m    \u001b[49m\u001b[43mbody\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmaybe_transform\u001b[49m\u001b[43m(\u001b[49m\u001b[43mparams\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43membedding_create_params\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mEmbeddingCreateParams\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    127\u001b[0m \u001b[43m    \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mmake_request_options\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    128\u001b[0m \u001b[43m        \u001b[49m\u001b[43mextra_headers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_headers\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    129\u001b[0m \u001b[43m        \u001b[49m\u001b[43mextra_query\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_query\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    130\u001b[0m \u001b[43m        \u001b[49m\u001b[43mextra_body\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mextra_body\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    131\u001b[0m \u001b[43m        \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    132\u001b[0m \u001b[43m        \u001b[49m\u001b[43mpost_parser\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mparser\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    133\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    134\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mCreateEmbeddingResponse\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    135\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m/opt/anaconda3/envs/llm-rag/lib/python3.12/site-packages/openai/_base_client.py:1278\u001b[0m, in \u001b[0;36mSyncAPIClient.post\u001b[0;34m(self, path, cast_to, body, options, files, stream, stream_cls)\u001b[0m\n\u001b[1;32m   1264\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mpost\u001b[39m(\n\u001b[1;32m   1265\u001b[0m     \u001b[38;5;28mself\u001b[39m,\n\u001b[1;32m   1266\u001b[0m     path: \u001b[38;5;28mstr\u001b[39m,\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m   1273\u001b[0m     stream_cls: \u001b[38;5;28mtype\u001b[39m[_StreamT] \u001b[38;5;241m|\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m,\n\u001b[1;32m   1274\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m ResponseT \u001b[38;5;241m|\u001b[39m _StreamT:\n\u001b[1;32m   1275\u001b[0m     opts \u001b[38;5;241m=\u001b[39m FinalRequestOptions\u001b[38;5;241m.\u001b[39mconstruct(\n\u001b[1;32m   1276\u001b[0m         method\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mpost\u001b[39m\u001b[38;5;124m\"\u001b[39m, url\u001b[38;5;241m=\u001b[39mpath, json_data\u001b[38;5;241m=\u001b[39mbody, files\u001b[38;5;241m=\u001b[39mto_httpx_files(files), \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39moptions\n\u001b[1;32m   1277\u001b[0m     )\n\u001b[0;32m-> 1278\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m cast(ResponseT, \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mrequest\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mopts\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m)\u001b[49m)\n",
      "File \u001b[0;32m/opt/anaconda3/envs/llm-rag/lib/python3.12/site-packages/openai/_base_client.py:955\u001b[0m, in \u001b[0;36mSyncAPIClient.request\u001b[0;34m(self, cast_to, options, remaining_retries, stream, stream_cls)\u001b[0m\n\u001b[1;32m    952\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m    953\u001b[0m     retries_taken \u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m\n\u001b[0;32m--> 955\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m    956\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    957\u001b[0m \u001b[43m    \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    958\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    959\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    960\u001b[0m \u001b[43m    \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m    961\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m/opt/anaconda3/envs/llm-rag/lib/python3.12/site-packages/openai/_base_client.py:1044\u001b[0m, in \u001b[0;36mSyncAPIClient._request\u001b[0;34m(self, cast_to, options, retries_taken, stream, stream_cls)\u001b[0m\n\u001b[1;32m   1042\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m remaining_retries \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_should_retry(err\u001b[38;5;241m.\u001b[39mresponse):\n\u001b[1;32m   1043\u001b[0m     err\u001b[38;5;241m.\u001b[39mresponse\u001b[38;5;241m.\u001b[39mclose()\n\u001b[0;32m-> 1044\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_retry_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m   1045\u001b[0m \u001b[43m        \u001b[49m\u001b[43minput_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1046\u001b[0m \u001b[43m        \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1047\u001b[0m \u001b[43m        \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1048\u001b[0m \u001b[43m        \u001b[49m\u001b[43mresponse_headers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43merr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresponse\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1049\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1050\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1051\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m   1053\u001b[0m \u001b[38;5;66;03m# If the response is streamed then we need to explicitly read the response\u001b[39;00m\n\u001b[1;32m   1054\u001b[0m \u001b[38;5;66;03m# to completion before attempting to access the response text.\u001b[39;00m\n\u001b[1;32m   1055\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m err\u001b[38;5;241m.\u001b[39mresponse\u001b[38;5;241m.\u001b[39mis_closed:\n",
      "File \u001b[0;32m/opt/anaconda3/envs/llm-rag/lib/python3.12/site-packages/openai/_base_client.py:1093\u001b[0m, in \u001b[0;36mSyncAPIClient._retry_request\u001b[0;34m(self, options, cast_to, retries_taken, response_headers, stream, stream_cls)\u001b[0m\n\u001b[1;32m   1089\u001b[0m \u001b[38;5;66;03m# In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a\u001b[39;00m\n\u001b[1;32m   1090\u001b[0m \u001b[38;5;66;03m# different thread if necessary.\u001b[39;00m\n\u001b[1;32m   1091\u001b[0m time\u001b[38;5;241m.\u001b[39msleep(timeout)\n\u001b[0;32m-> 1093\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m   1094\u001b[0m \u001b[43m    \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1095\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1096\u001b[0m \u001b[43m    \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1097\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1098\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1099\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m/opt/anaconda3/envs/llm-rag/lib/python3.12/site-packages/openai/_base_client.py:1044\u001b[0m, in \u001b[0;36mSyncAPIClient._request\u001b[0;34m(self, cast_to, options, retries_taken, stream, stream_cls)\u001b[0m\n\u001b[1;32m   1042\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m remaining_retries \u001b[38;5;241m>\u001b[39m \u001b[38;5;241m0\u001b[39m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_should_retry(err\u001b[38;5;241m.\u001b[39mresponse):\n\u001b[1;32m   1043\u001b[0m     err\u001b[38;5;241m.\u001b[39mresponse\u001b[38;5;241m.\u001b[39mclose()\n\u001b[0;32m-> 1044\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_retry_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m   1045\u001b[0m \u001b[43m        \u001b[49m\u001b[43minput_options\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1046\u001b[0m \u001b[43m        \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1047\u001b[0m \u001b[43m        \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1048\u001b[0m \u001b[43m        \u001b[49m\u001b[43mresponse_headers\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43merr\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mresponse\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mheaders\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1049\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1050\u001b[0m \u001b[43m        \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1051\u001b[0m \u001b[43m    \u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m   1053\u001b[0m \u001b[38;5;66;03m# If the response is streamed then we need to explicitly read the response\u001b[39;00m\n\u001b[1;32m   1054\u001b[0m \u001b[38;5;66;03m# to completion before attempting to access the response text.\u001b[39;00m\n\u001b[1;32m   1055\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m err\u001b[38;5;241m.\u001b[39mresponse\u001b[38;5;241m.\u001b[39mis_closed:\n",
      "File \u001b[0;32m/opt/anaconda3/envs/llm-rag/lib/python3.12/site-packages/openai/_base_client.py:1093\u001b[0m, in \u001b[0;36mSyncAPIClient._retry_request\u001b[0;34m(self, options, cast_to, retries_taken, response_headers, stream, stream_cls)\u001b[0m\n\u001b[1;32m   1089\u001b[0m \u001b[38;5;66;03m# In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a\u001b[39;00m\n\u001b[1;32m   1090\u001b[0m \u001b[38;5;66;03m# different thread if necessary.\u001b[39;00m\n\u001b[1;32m   1091\u001b[0m time\u001b[38;5;241m.\u001b[39msleep(timeout)\n\u001b[0;32m-> 1093\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_request\u001b[49m\u001b[43m(\u001b[49m\n\u001b[1;32m   1094\u001b[0m \u001b[43m    \u001b[49m\u001b[43moptions\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43moptions\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1095\u001b[0m \u001b[43m    \u001b[49m\u001b[43mcast_to\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcast_to\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1096\u001b[0m \u001b[43m    \u001b[49m\u001b[43mretries_taken\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mretries_taken\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[38;5;241;43m1\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1097\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1098\u001b[0m \u001b[43m    \u001b[49m\u001b[43mstream_cls\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mstream_cls\u001b[49m\u001b[43m,\u001b[49m\n\u001b[1;32m   1099\u001b[0m \u001b[43m\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[0;32m/opt/anaconda3/envs/llm-rag/lib/python3.12/site-packages/openai/_base_client.py:1059\u001b[0m, in \u001b[0;36mSyncAPIClient._request\u001b[0;34m(self, cast_to, options, retries_taken, stream, stream_cls)\u001b[0m\n\u001b[1;32m   1056\u001b[0m         err\u001b[38;5;241m.\u001b[39mresponse\u001b[38;5;241m.\u001b[39mread()\n\u001b[1;32m   1058\u001b[0m     log\u001b[38;5;241m.\u001b[39mdebug(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mRe-raising status error\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n\u001b[0;32m-> 1059\u001b[0m     \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_make_status_error_from_response(err\u001b[38;5;241m.\u001b[39mresponse) \u001b[38;5;28;01mfrom\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m   1061\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_process_response(\n\u001b[1;32m   1062\u001b[0m     cast_to\u001b[38;5;241m=\u001b[39mcast_to,\n\u001b[1;32m   1063\u001b[0m     options\u001b[38;5;241m=\u001b[39moptions,\n\u001b[0;32m   (...)\u001b[0m\n\u001b[1;32m   1067\u001b[0m     retries_taken\u001b[38;5;241m=\u001b[39mretries_taken,\n\u001b[1;32m   1068\u001b[0m )\n",
      "\u001b[0;31mRateLimitError\u001b[0m: Error code: 429 - {'error': {'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors.', 'type': 'insufficient_quota', 'param': None, 'code': 'insufficient_quota'}}"
     ]
    }
   ],
   "source": [
    "import os\n",
    "from openai import OpenAI\n",
    "from dotenv import load_dotenv, find_dotenv\n",
    "\n",
    "\n",
    "# 读取本地/项目的环境变量。\n",
    "# find_dotenv()寻找并定位.env文件的路径\n",
    "# load_dotenv()读取该.env文件，并将其中的环境变量加载到当前的运行环境中  \n",
    "# 如果你设置的是全局的环境变量，这行代码则没有任何作用。\n",
    "_ = load_dotenv(find_dotenv())\n",
    "\n",
    "\n",
    "def openai_embedding(text: str, model: str=None):\n",
    "    # 获取环境变量 OPENAI_API_KEY\n",
    "    api_key=os.environ['OPENAI_API_KEY']\n",
    "    client = OpenAI(api_key=api_key)\n",
    "\n",
    "    # embedding model：'text-embedding-3-small', 'text-embedding-3-large', 'text-embedding-ada-002'\n",
    "    if model == None:\n",
    "        model=\"text-embedding-3-small\"\n",
    "\n",
    "    response = client.embeddings.create(\n",
    "        input=text,\n",
    "        model=model\n",
    "    )\n",
    "    return response\n",
    "\n",
    "response = openai_embedding(text='要生成 embedding 的输入文本，字符串形式。')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "API返回的数据为`json`格式，除`object`向量类型外还有存放数据的`data`、embedding model 型号`model`以及本次 token 使用情况`usage`等数据，具体如下所示：\n",
    "```json\n",
    "{\n",
    "  \"object\": \"list\",\n",
    "  \"data\": [\n",
    "    {\n",
    "      \"object\": \"embedding\",\n",
    "      \"index\": 0,\n",
    "      \"embedding\": [\n",
    "        -0.006929283495992422,\n",
    "        ... (省略)\n",
    "        -4.547132266452536e-05,\n",
    "      ],\n",
    "    }\n",
    "  ],\n",
    "  \"model\": \"text-embedding-3-small\",\n",
    "  \"usage\": {\n",
    "    \"prompt_tokens\": 5,\n",
    "    \"total_tokens\": 5\n",
    "  }\n",
    "}\n",
    "```\n",
    "我们可以调用response的object来获取embedding的类型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "返回的embedding类型为：list\n"
     ]
    }
   ],
   "source": [
    "print(f'返回的embedding类型为：{response.object}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "embedding存放在data中，我们可以查看embedding的长度及生成的embedding。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "embedding长度为：1536\n",
      "embedding（前10）为：[0.03884002938866615, 0.013516489416360855, -0.0024250170681625605, -0.01655769906938076, 0.024130908772349358, -0.017382603138685226, 0.04206013306975365, 0.011498954147100449, -0.028245486319065094, -0.00674333656206727]\n"
     ]
    }
   ],
   "source": [
    "print(f'embedding长度为：{len(response.data[0].embedding)}')\n",
    "print(f'embedding（前10）为：{response.data[0].embedding[:10]}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "我们也可以查看此次embedding的模型及token使用情况。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "本次embedding model为：text-embedding-3-small\n",
      "本次token使用情况为：Usage(prompt_tokens=12, total_tokens=12)\n"
     ]
    }
   ],
   "source": [
    "print(f'本次embedding model为：{response.model}')\n",
    "print(f'本次token使用情况为：{response.usage}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "import json\n",
    "\n",
    "def wenxin_embedding(text: str):\n",
    "    # 获取环境变量 wenxin_api_key、wenxin_secret_key\n",
    "    api_key = os.environ['QIANFAN_AK']\n",
    "    secret_key = os.environ['QIANFAN_SK']\n",
    "\n",
    "    # 使用API Key、Secret Key向https://aip.baidubce.com/oauth/2.0/token 获取Access token\n",
    "    url = \"https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id={0}&client_secret={1}\".format(api_key, secret_key)\n",
    "    payload = json.dumps(\"\")\n",
    "    headers = {\n",
    "        'Content-Type': 'application/json',\n",
    "        'Accept': 'application/json'\n",
    "    }\n",
    "    response = requests.request(\"POST\", url, headers=headers, data=payload)\n",
    "    \n",
    "    # 通过获取的Access token 来embedding text\n",
    "    url = \"https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/embeddings/embedding-v1?access_token=\" + str(response.json().get(\"access_token\"))\n",
    "    input = []\n",
    "    input.append(text)\n",
    "    payload = json.dumps({\n",
    "        \"input\": input\n",
    "    })\n",
    "    headers = {\n",
    "        'Content-Type': 'application/json'\n",
    "    }\n",
    "\n",
    "    response = requests.request(\"POST\", url, headers=headers, data=payload)\n",
    "\n",
    "    return json.loads(response.text)\n",
    "# text应为List(string)\n",
    "text = \"要生成 embedding 的输入文本，字符串形式。\"\n",
    "response = wenxin_embedding(text=text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Embedding-V1每次embedding除了有单独的id外，还有时间戳记录embedding的时间。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "本次embedding id为：as-hvbgfuk29u\n",
      "本次embedding产生时间戳为：1711435238\n"
     ]
    }
   ],
   "source": [
    "print('本次embedding id为：{}'.format(response['id']))\n",
    "print('本次embedding产生时间戳为：{}'.format(response['created']))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "同样的我们也可以从response中获取embedding的类型和embedding。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "返回的embedding类型为:embedding_list\n",
      "embedding长度为：384\n",
      "embedding（前10）为：[0.060567744076251984, 0.020958080887794495, 0.053234219551086426, 0.02243831567466259, -0.024505289271473885, -0.09820500761270523, 0.04375714063644409, -0.009092536754906178, -0.020122773945331573, 0.015808865427970886]\n"
     ]
    }
   ],
   "source": [
    "print('返回的embedding类型为:{}'.format(response['object']))\n",
    "print('embedding长度为：{}'.format(len(response['data'][0]['embedding'])))\n",
    "print('embedding（前10）为：{}'.format(response['data'][0]['embedding'][:10]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "尚未开放"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用智谱API\n",
    "智谱有封装好的SDK，我们调用即可。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "\n",
    "from dotenv import load_dotenv, find_dotenv\n",
    "\n",
    "# 读取本地/项目的环境变量。\n",
    "# find_dotenv() 寻找并定位 .env 文件的路径\n",
    "# load_dotenv() 读取该 .env 文件，并将其中的环境变量加载到当前的运行环境中  \n",
    "_ = load_dotenv(find_dotenv())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "from zhipuai import ZhipuAI\n",
    "def zhipu_embedding(text: str):\n",
    "    api_key = os.environ['ZHIPUAI_API_KEY']\n",
    "    client = ZhipuAI(api_key=api_key)\n",
    "    response = client.embeddings.create(\n",
    "        model=\"embedding-2\",\n",
    "        input=text,\n",
    "    )\n",
    "    return response\n",
    "\n",
    "text = '这是一个测试句子。'\n",
    "response = zhipu_embedding(text=text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "response为`zhipuai.types.embeddings.EmbeddingsResponded`类型，我们可以调用`object`、`data`、`model`、`usage`来查看response的embedding类型、embedding、embedding model及使用情况。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "response类型为：<class 'zhipuai.types.embeddings.EmbeddingsResponded'>\n",
      "embedding类型为：list\n",
      "生成embedding的model为：embedding-2\n",
      "生成的embedding长度为：1024\n",
      "embedding（前10）为: [-0.016744893, 0.024574783, -0.049725085, -0.047218937, 0.05545208, -0.015653659, -0.015191118, -0.030362211, 0.018612906, 0.04555603]\n"
     ]
    }
   ],
   "source": [
    "print(f'response类型为：{type(response)}')\n",
    "print(f'embedding类型为：{response.object}')\n",
    "print(f'生成embedding的model为：{response.model}')\n",
    "print(f'生成的embedding长度为：{len(response.data[0].embedding)}')\n",
    "print(f'embedding（前10）为: {response.data[0].embedding[:10]}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Document embeddings:\n",
      "Document 1 embedding: [0.037720047, 0.06462046, -0.051727317, -0.003894384, 0.052013297, -6.842158e-05, -0.03134974, 0.0029348186, 0.0044539482, 0.07279294]\n",
      "Document 2 embedding: [0.02188894, 0.08218758, -0.037135124, 0.021887042, 0.041882645, -0.033520482, -0.03491134, -0.015874526, 0.014798602, 0.0546242]\n",
      "\n",
      "Query embedding:\n",
      "Query embedding: [0.054289814, 0.072955854, -0.048414953, 0.054884505, 0.039544903, -0.0016492179, -0.0363829, 0.011582962, 0.02993188, 0.052928533]\n"
     ]
    }
   ],
   "source": [
    "from langchain_community.embeddings import ZhipuAIEmbeddings\n",
    "\n",
    "zhipu_embed = ZhipuAIEmbeddings(\n",
    "    model=\"embedding-2\",\n",
    "    api_key=\"5713143e8fdc4b4a8b284cf97092e70f.qEK71mGIlavzO1Io\",\n",
    ")\n",
    "r1 = zhipu_embed.embed_documents(\n",
    "    [\n",
    "        \"Alpha is the first letter of Greek alphabet\",\n",
    "        \"Beta is the second letter of Greek alphabet\",\n",
    "    ]\n",
    ")\n",
    "r2 = zhipu_embed.embed_query(\n",
    "    \"What is the second letter of Greek alphabet\"\n",
    ")\n",
    "\n",
    "# 打印结果\n",
    "print(\"Document embeddings:\")\n",
    "for i, embedding in enumerate(r1):\n",
    "    print(f\"Document {i+1} embedding: {embedding[:10]}\")\n",
    "\n",
    "print(\"\\nQuery embedding:\")\n",
    "print(f\"Query embedding: {r2[:10]}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用HuggingFaceEmbeddings"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "使用 langchain_community 库中的 HuggingFaceEmbeddings 类来加载一个预训练的模型 \"sentence-transformers/all-mpnet-base-v2\"。\n",
    "这个模型是基于 MPNet 架构的，能够将文本转换为密集向量（即嵌入），这些向量可以用于各种自然语言处理任务，如语义相似度计算、文本分类等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.embeddings import HuggingFaceEmbeddings\n",
    "\n",
    "model_name = \"sentence-transformers/all-mpnet-base-v2\"\n",
    "model_kwargs = {'device': 'cpu'}\n",
    "encode_kwargs = {'normalize_embeddings': False}\n",
    "hf = HuggingFaceEmbeddings(\n",
    "    model_name=model_name,\n",
    "    model_kwargs=model_kwargs,\n",
    "    encode_kwargs=encode_kwargs\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "model_name: 指定了要使用的预训练模型的名称。这里使用的是 sentence-transformers/all-mpnet-base-v2，这是一个非常强大的句子编码器。  \n",
    "model_kwargs: 这是一个字典，可以用来传递额外的关键字参数给模型。在这里，你指定了设备为 'cpu'，这意味着模型将在 CPU 上运行而不是 GPU。  \n",
    "encode_kwargs: 这个参数允许你在编码过程中指定额外的行为。例如，normalize_embeddings=False 表示生成的嵌入不会被归一化。  "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[0.018255455419421196, -0.07722701877355576, -0.025231460109353065, 0.03972402215003967, 0.014017574489116669, 0.02099592052400112, -0.05323655158281326, 0.0696718767285347, 0.010698503814637661, 0.00441876333206892]\n"
     ]
    }
   ],
   "source": [
    "# 输出与输入句子相对应的嵌入向量\n",
    "text = \"这是一个测试句子。\"\n",
    "embedding = hf.embed_query(text)\n",
    "print(embedding[:10])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "句子 1 的嵌入: [0.017014818266034126, -0.07300769537687302, -0.024362027645111084, 0.036770958453416824, 0.018084252253174782, 0.024283571168780327, -0.051937445998191833, 0.07036285102367401, 0.009358232840895653, 0.0036771756131201982]\n",
      "句子 2 的嵌入: [0.05256107077002525, -0.03201902657747269, -0.017431410029530525, 0.034521088004112244, 0.000509809295181185, 0.021155573427677155, -0.024982864037156105, 0.06683092564344406, 0.011538864113390446, 0.009967169724404812]\n"
     ]
    }
   ],
   "source": [
    "# 如果有多个文档需要嵌入，可以使用 embed_documents 方法\n",
    "texts = [\"这是第一个句子。\", \"这是第二个句子。\"]\n",
    "embeddings = hf.embed_documents(texts)\n",
    "for i, emb in enumerate(embeddings):\n",
    "    print(f\"句子 {i+1} 的嵌入: {emb[:10]}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 使用ollama Embeddings"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_community.embeddings import OllamaEmbeddings\n",
    "ollama_emb = OllamaEmbeddings(\n",
    "    base_url='http://localhost:11434',\n",
    "    model=\"nomic-embed-text:latest\"\n",
    ")\n",
    "r1 = ollama_emb.embed_documents(\n",
    "    [\n",
    "        \"Alpha is the first letter of Greek alphabet\",\n",
    "        \"Beta is the second letter of Greek alphabet\",\n",
    "    ]\n",
    ")\n",
    "r2 = ollama_emb.embed_query(\n",
    "    \"What is the second letter of Greek alphabet\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Document embeddings:\n",
      "Document 1 embedding: [-0.12490570545196533, 0.628205418586731, -3.551535129547119, -2.0412728786468506, 1.4103292226791382, -0.17636485397815704, -0.39113128185272217, 0.4969802498817444, -0.22523435950279236, -0.1942846029996872]\n",
      "Document 2 embedding: [0.3750990927219391, 0.9479906558990479, -3.3803842067718506, -1.5132076740264893, 1.9580979347229004, -0.2362193763256073, -0.047427646815776825, -0.3607965409755707, -0.6280883550643921, -0.5543370842933655]\n",
      "\n",
      "Query embedding:\n",
      "Query embedding: [0.5795260667800903, 0.08798552304506302, -3.2944631576538086, -1.6638518571853638, 1.7470965385437012, -0.9476922154426575, -0.839259147644043, -0.019277475774288177, -0.2768111228942871, -0.3237455487251282]\n"
     ]
    }
   ],
   "source": [
    "# 打印结果\n",
    "print(\"Document embeddings:\")\n",
    "for i, embedding in enumerate(r1):\n",
    "    print(f\"Document {i+1} embedding: {embedding[:10]}\")\n",
    "\n",
    "print(\"\\nQuery embedding:\")\n",
    "print(f\"Query embedding: {r2[:10]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# xinference embedding"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain",
   "language": "python",
   "name": "langchain"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
